#ifndef TABLE_H
#define TABLE_H

#include <QSqlDatabase>
#include <QSqlQuery>
#include <QObject>
#include <QHash>
#include <QMap>
#include "Field.h"

#define FIELD(type,name) Q_PROPERTY(type name READ name)\
    type name;
#define NOTNULL(type,name) FIELD(type,name)\
    Q_PROPERTY(char notnull_##name)
#define UNIQUE(type,name) NOTNULL(type,name)\
    Q_PROPERTY(char unique_##name)
#define UNIQUE_NULL(type,name) FIELD(type,name)\
    Q_PROPERTY(char unique_##name)
#define PRIMARYKEY(type,name) FIELD(type,name)\
    Q_PROPERTY(char primarykey_##name)

#define TABLE(name) name() : QyOrm::Table(){initialize();}\
    name& setField(const QString& fieldName, const QVariant &value){setFieldValue(fieldName, value);return*this;}

#define FOREIGN(type, name, foreign) NOTNULL(type,name)\
    Q_PROPERTY(short name##_##foreign)
#define RegisterForeign(T) QyOrm::Table::registerTable<T>()
/*
*/
#define QyOrmSelect QyOrm::Table::select
#define QyOrmSelectOne QyOrm::Table::selectOne
#define QyOrmInstance(T,key) dynamic_cast<T*>(QyOrm::Table::searchInstance(#T, key))
#define QyOrmInstanceNotString(T,key) dynamic_cast<T*>(QyOrm::Table::searchInstance(#T, key, false))
class QSqlDatabase;
namespace QyOrm {

class Table : public QObject
{
    Q_OBJECT
public:
    explicit Table();
    void* operator new(size_t size);
    void initialize();
    virtual ~Table();

public:
    bool update(Field*field);
    template<class T>static QList<Table*> selectTable(const T& table);
    template<class T>static QList<T*> select(const T& table);
    template<class T>static T* selectOne(const T& table);
    bool insert();
    bool remove();
    bool create();
    QString toJson();
    static QString toJson(QList<Table*> tables);
    bool fromJson(QJsonObject* object);
public:
    bool isInstance();
    void setFieldValue(const QString& fieldName, const QVariant &value);
public:
    typedef QList<Table*> (*Constructor)(const QVariant& var);
    template<typename T>static bool registerTable(){
        constructors().insert( T::staticMetaObject.className(), &constructorHelper<T> );return true;
    }
    static QList<Table*> createTable(const QString& className, const QVariant& var);
    template<typename T>static QList<Table*> constructorHelper(const QVariant& var){ if(var.isNull())return{new T()};T t;t.primaryKey->query(var);return selectTable<T>(t); }
    static QHash<QString, Constructor>& constructors();
    bool foreignInited = false;
public:
    static QSqlDatabase db;
    static QSqlDatabase database(bool isDelete = false);
    Field* primaryKey = nullptr;
    QString tableName;
public:
    QStringList listField;
    QHash<QString, Field*> hashField;
    QHash<QString, QString> hashForeign;
protected:
    static QMap<QString, Table*> hashTable;//对象唯一
    QString instanceKey;
    Table *createInstance();
    bool atHeap = false;//是否是在堆上
    bool instance = false;//是否是数据库的实际值
public:
    static Table* searchInstance(const QString& tableName, const QString& primaryKey, bool isString = true);
    static void destoryAllInstance();
};

template<class T>
QList<Table *> Table::selectTable(const T& table)
{
    QList<Table*> result;
    static_assert(std::is_base_of<Table, T>::value, "T must base of Table!");
    QString sql = "select * from " + table.tableName;
    QString where;
    for(Field* field : table.hashField.values()){
        if(!field->isNull){
            where += (where.isEmpty() ? " " : " and ") + field->execute();
        }
    }
    if(!where.isEmpty()){
        sql += " where" + where;
    }
    QSqlQuery query(database());
    if(query.exec(sql)){
        while (query.next()) {
            Table* tmp = new T();
            int cursor = -1;
            for(QString& fieldName : tmp->listField){
                tmp->hashField.value(fieldName)->query(query.value(++cursor));
            }
            Table* ins = tmp->createInstance();
            if(ins != nullptr){
                delete tmp;tmp = ins;
            }
            result.append(tmp);
        }
    }
    return result;
}

template<class T>
QList<T *> Table::select(const T& table)
{
    QList<T*> result;
    for(Table* t : selectTable(table)){
        result.append(dynamic_cast<T*>(t));
    }
    return result;
}

template<class T>
T *Table::selectOne(const T &table)
{
    QList<T*> result = select(table);
    if(result.size() > 0){
        return result.first();
    }else{
        return nullptr;
    }
}

}

#endif // TABLE_H
